/**
 * 
 */
package com.bloomingstars.sqm.orm;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;




import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * Hibernate implementation of GenericDao
 * A typesafe implementation of CRUD and finder methods based on Hibernate 
 */
public abstract class GenericDaoHibernateImpl<T, PK extends Serializable> 
    implements GenericDao<T, PK> 
{
    private static final Logger logger = LogManager.getLogger("GenericDaoHibernateImpl"); 
    
    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> type)
    {
        this.type = type;
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public PK create(T o)
    {        
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        PK result = null;
        
        try {
            tx = session.beginTransaction();
            result = (PK) session.save(o);
            tx.commit();
        } catch (RuntimeException rex) {
            if (tx.isActive()) tx.rollback();
            throw rex;
        }
        return result;
    }

    @Override
    public void saveOrUpdate(T transientObject) {
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        
        try {
            tx = session.beginTransaction();
            session.saveOrUpdate(transientObject);
            tx.commit();
        } catch (RuntimeException rex) {
            if (tx.isActive()) tx.rollback();
            throw rex;
        }
    }    
    
    @Override
    @SuppressWarnings("unchecked")
    public T read(PK id)
    {
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        T result = null;
        try {
            tx = session.beginTransaction();
            result = (T) session.get(type, id);
            tx.commit();                    
        } catch (RuntimeException rex) {
            if (tx != null && tx.isActive()) tx.rollback();
            throw rex;
        }
                
        return result;       
    }

    @Override
    public void update(T o)
    {
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.update(o);
            tx.commit();
        } catch (NonUniqueObjectException nonex) {
            if (tx != null && tx.isActive()) tx.rollback();
            // Attempt a merge
            try {
                session = HibernateUtil.getSession();
                tx = session.beginTransaction();
                session.merge(o);
                tx.commit();
            } catch (RuntimeException rex) {
                if (tx.isActive()) tx.rollback();
                throw rex;
            }
        } catch (RuntimeException rex) {
            if (tx.isActive()) tx.rollback();
            throw rex;
        }        
    }

    @Override
    public void delete(T o)
    {
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.delete(o);
            tx.commit();
        } catch (RuntimeException rex) {
            if (tx != null && tx.isActive()) tx.rollback();
            throw rex;
        }
    }
    
    @Override
    public void delete(PK id) {                        
        T persistent = read(id);
        
        Session session = HibernateUtil.getSession();
        if (persistent != null) {
            Transaction tx = null;
            try {
                tx = session.beginTransaction();
                session.delete(persistent);
                tx.commit();
            } catch (RuntimeException rex) {
                if (tx != null && tx.isActive()) tx.rollback();
                throw rex;
            }           
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<T> findLike(T example) {        
        Session session = HibernateUtil.getSession();                
        List<T> results = null;
        
        Transaction tx = null;        
        try {
            tx = session.beginTransaction();
            Criteria criteria = session.createCriteria(type);
            addRestrictionsToExample(criteria, example);
            results = (List<T>) criteria.list();            
            tx.commit();
        } catch (RuntimeException rex) {
            if (tx != null && tx.isActive()) tx.rollback();
            throw rex;            
        }
                
        return results;
    }

    @Override
    public boolean existsLike(T example) {
        List<T> results = findLike(example);
        
        return (results.size() > 0);
    }

    @Override
    public boolean exists(PK id) {
        T obj = read(id);
        boolean result = (obj != null);        
        if (obj != null) {
            Session session = HibernateUtil.getSession();
            
            Transaction tx = null;
            try {
                tx = session.beginTransaction();
                session.evict(obj);
                tx.commit();
            } catch (RuntimeException rex) {
                if (tx != null && tx.isActive()) tx.rollback();
                throw rex;
            }            
        }
        
        return result;
    }
    
    @Override
    public void resetSession(Session session) {
        try {
            session.flush();
            session.close();            
        } catch (Exception ex) {
            logger.error("Exception raised: " + ex);
            logger.error(ex);
        }
    }
    
    @Override
    public void resetSession() {
        try {
            Session session = HibernateUtil.getSession();
            session.flush();
            session.close();            
        } catch (Exception ex) {
            logger.error("Exception raised: " + ex);
            logger.error(ex);            
        } finally {
            HibernateUtil.getSession();
        }        
    }    
    
    @Override
    @SuppressWarnings("unchecked")
    public List<PK> createList(List<T> list) {
        Session session = HibernateUtil.getSession();
        Transaction tx = null;
        List<PK> result = new ArrayList<PK>();
        
        try {
            tx = session.beginTransaction();
            for (T item: list) {
                PK pk = (PK) session.save(item);
                result.add(pk);
            }
            tx.commit();
        } catch (RuntimeException rex) {
            if (tx.isActive()) tx.rollback();
            throw rex;
        }        
        return result;
    }
    
    protected Class<T> getType() {
        return type;
    }
}